// 防止头文件被多次包含的宏守卫
#pragma once
// 包含自定义的锁卫士头文件，用于 RAII 风格的互斥量管理
#include "LockGuard.hpp"
// 包含自定义的日志库头文件
#include "Log.hpp"
// 包含自定义的线程封装类头文件
// 注意：这里包含的是 Thread.hpp，它定义了模板化的 Thread<T> 类
#include "Thread.hpp"

// 包含标准输入输出流库
#include <iostream>
// 包含标准向量库，用于存储工作线程对象
#include <vector>
// 包含标准队列库，用于存储待执行的任务 (任务队列)
#include <queue>
// 包含 POSIX 线程库，用于 pthread_mutex_t 和 pthread_cond_t
#include <pthread.h>
// 使用 ThreadModule 命名空间，可以直接使用其中的 Thread 类
using namespace ThreadModule;
// 使用 std 命名空间，可以直接使用 std::cout, std::queue, std::vector 等
using namespace std;

// 默认线程池中的工作线程数量常量
const static int gdefaultthreadnum = 10;

// 线程池模板类，T 表示任务的类型
// 用户需要定义自己的任务类型 T，并且该类型 T 需要是“可调用的”（比如重载了 operator()）
// 并且可能需要有方法来表示执行结果，如 ResultToString() (见 HandlerTask 中的调用)
template <typename T>
class ThreadPool
{
private:
    // === 私有辅助方法：封装底层的同步原语操作 ===

    // 加锁操作，保护对任务队列 _task_queue 的并发访问
    void LockQueue()
    {
        // 调用 POSIX 互斥锁的加锁函数
        pthread_mutex_lock(&_mutex);
        // 实际项目中通常需要检查返回值，这里简化了
    }
    // 解锁操作，释放对任务队列的访问权限
    void UnlockQueue()
    {
        // 调用 POSIX 互斥锁的解锁函数
        pthread_mutex_unlock(&_mutex);
        // 实际项目中通常需要检查返回值
    }
    // 线程休眠，在条件变量上等待任务
    // 调用此函数前必须持有 _mutex 锁
    void ThreadSleep()
    {
        // 调用 POSIX 条件变量的等待函数
        // 会原子地释放 _mutex 锁，并将当前线程放入 _cond 的等待队列，然后线程阻塞
        // 当线程被唤醒时，函数返回前会自动重新获取 _mutex 锁
        pthread_cond_wait(&_cond, &_mutex);
        // 实际项目中通常需要检查返回值
        // 注意：pthread_cond_wait 可能发生假唤醒，因此需要在循环中检查条件
    }
    // 唤醒一个等待在条件变量上的线程
    void ThreadWakeup()
    {
        // 调用 POSIX 条件变量的 signal 函数
        // 唤醒等待队列中的一个线程（如果存在）
        pthread_cond_signal(&_cond);
        // 实际项目中通常需要检查返回值
    }
    // 唤醒所有等待在条件变量上的线程
    void ThreadWakeupAll()
    {
        // 调用 POSIX 条件变量的 broadcast 函数
        // 唤醒等待队列中的所有线程（如果存在）
        pthread_cond_broadcast(&_cond);
        // 实际项目中通常需要检查返回值
    }

    // === 私有构造函数：实现单例模式的关键 ===
    // 构造函数被声明为私有，这意味着外部代码不能直接创建 ThreadPool 对象
    // 只能通过静态方法 GetInstance 来获取唯一的实例
    // @param threadnum: 线程池中的工作线程数量，默认为 gdefaultthreadnum
    ThreadPool(int threadnum = gdefaultthreadnum)
        // 初始化列表，初始化成员变量
        : _threadnum(threadnum), // 初始化线程数量
          _waitnum(0),           // 初始化等待任务的线程数量为 0
          _isrunning(false)      // 初始化线程池运行状态为 false (尚未启动)
    {
        // 在构造函数中初始化 POSIX 互斥锁和条件变量
        pthread_mutex_init(&_mutex, nullptr); // 初始化互斥锁，nullptr 表示默认属性
        pthread_cond_init(&_cond, nullptr);   // 初始化条件变量，nullptr 表示默认属性
        // 实际项目中通常需要检查初始化函数的返回值
        LOG(INFO, "ThreadPool Construct() - Initializing mutex and condition variable");
    }

    // 初始化线程池，创建所有工作线程对象
    // 注意：这里只是创建 Thread 对象，并未启动底层 POSIX 线程
    void InitThreadPool()
    {
        // 指向构建出所有的线程对象，并将它们存储在 _threads 向量中
        for (int num = 0; num < _threadnum; num++)
        {
            // 生成线程名称
            std::string name = "thread-" + std::to_string(num + 1);
            // 创建 Thread 对象并添加到 _threads 向量
            // 注意：此处对 Thread 类的使用看起来与前面注释的 Thread<T> 定义存在类型或参数不匹配问题
            // 前面的 Thread<T> 构造函数签名是 Thread(func_t<T> func, T &data, std::string name = "none-name")
            // 其中 func_t<T> 是 std::function<void(T &, std::string name)>
            // HandlerTask 的签名是 void HandlerTask(std::string name)
            // std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1) 创建的可调用对象
            // 当被 Thread::Excute 调用时，Excute 会传递两个参数：_data (T&, from Thread object) 和 _threadname (std::string)
            // 这个 bind 对象期望其第一个参数 (由 placeholders::_1 占位) 对应 Thread::Excute 传入的 _data (T&)，
            // 并将 Thread::Excute 传入的 _threadname 传递给 HandlerTask 的 std::string name 参数。
            // 但 HandlerTask 的签名不匹配！它只接收一个 std::string name。
            // 此外，Thread 构造函数需要一个 T& data 参数，这里没有提供。
            // 这表明此处代码的 Thread 类使用方式与之前定义的 Thread<T> 类不兼容，或者 Thread 向量缺失模板参数，
            // 或者 Thread 类需要重新设计以适应 Worker 线程的主循环模式。
            // 我们先按照代码原貌进行注释，并标记出这里可能的逻辑或类型问题。

            _threads.emplace_back(
                // 传递给 Thread 构造函数的可调用对象 (使用 bind 绑定成员函数和 this 指针)
                // ???此处bind的作用？？？ - std::bind 将 ThreadPool 类的成员函数 HandlerTask 绑定到当前的 ThreadPool 实例 (this)
                // std::placeholders::_1 是一个占位符，表示调用 bind 产生的对象时，其第一个参数将被传递给 HandlerTask (如果 HandlerTask 接受一个参数)
                // 然而，如上所述，HandlerTask 只接受一个 std::string，且 Thread 的 Excute 会传递 T& 和 std::string。这里存在签名不匹配。
                // 假设原意是 Worker 线程的主循环是 HandlerTask(string name)，并且这个 Thread 类设计用来运行这种签名的函数：
                // 那么 Thread 类可能需要定义为 Thread<std::string> 或 Thread<void>, func_t 可能需要调整。
                // 目前代码逻辑不清晰如何将 HandlerTask 签名与 Thread 类通用 func_t 匹配。
                std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1),
                // 传递给 Thread 构造函数的线程名称
                name, // 补上T& data 参数
                name
                // 注意：这里缺少了 Thread 构造函数所需的 T& data 参数
            );

            LOG(INFO, "init thread %s done", name.c_str());
        }
        // 初始化完成后，设置运行状态为 true
        _isrunning = true;
    }

    // 启动所有工作线程
    void Start()
    {
        // 遍历 _threads 向量，对每个 Thread 对象调用 Start() 方法，创建底层 POSIX 线程
        for (auto &thread : _threads)
        {
            thread.Start();
        }
        LOG(INFO, "ThreadPool Start() - All worker threads started.");
    }

    // 线程处理任务的主函数 - 这是每个工作线程执行的循环体
    // 这个函数会不断地从任务队列中获取任务并执行
    // @param name: 工作线程的名称 (由 Thread 对象传递进来)
    // (注意：此处 HandlerTask 的签名只接受 name，但 Bind 和 Thread 类设计可能期望其他参数)
    void HandlerTask(std::string name) // 类的成员方法，可以方便地访问 ThreadPool 对象的私有成员（任务队列、锁、条件变量等）
    {
        LOG(INFO, "%s is running...", name.c_str());
        // 工作线程的主循环，持续获取和处理任务
        while (true)
        {
            // --- 任务获取临界区开始 ---
            // 1. 保证队列安全 - 锁定互斥量，保护对任务队列和状态变量的访问
            LockQueue();

            // 2. 检查任务队列是否为空 && 线程池是否仍在运行
            // 如果队列为空且线程池正在运行，工作线程进入等待状态
            // 为什么是 while 循环？为了处理假唤醒。即使被唤醒，也需要再次检查条件是否真的满足。
            while (_task_queue.empty() && _isrunning)
            {
                _waitnum++; // 递增等待任务的线程数量 (在锁内操作，线程安全)
                // 线程休眠，在条件变量 _cond 上等待，并原子地释放 _mutex
                ThreadSleep();
                _waitnum--; // 被唤醒并重新获取锁后，递减等待任务的线程数量 (在锁内操作，线程安全)
            }

            // 2.1 检查退出条件：如果线程池已经停止运行 (`_isrunning` 为 false) && 任务队列是空的
            // 这意味着线程池已通知停止，且所有待处理任务已处理完毕。
            if (_task_queue.empty() && !_isrunning)
            {
                // 解锁并退出循环，线程将终止
                UnlockQueue();
                LOG(INFO, "%s exiting due to shutdown and empty queue.", name.c_str());
                break; // 退出 while 循环，线程函数返回
            }

            // 2.2 如果线程池不退出 (`_isrunning` 为 true) && 任务队列不是空的 - 正常情况，有任务可取
            // 2.3 如果线程池已经退出 (`_isrunning` 为 false) && 任务队列不是空的 - 线程池正在停止，但队列中还有任务，需要先处理完。
            // 这里的逻辑会继续处理队列中的任务，直到队列为空，然后才能满足 2.1 的退出条件。

            // 3. 执行到这里，说明队列中有任务，获取任务
            T t = _task_queue.front(); // 获取队列头部任务的拷贝 (注意任务类型 T 的拷贝/移动开销)
            _task_queue.pop();         // 从队列中移除头部任务

            // --- 任务获取临界区结束 ---
            // 获取到任务后，立即解锁，让其他线程可以访问队列
            UnlockQueue();

            LOG(DEBUG, "%s get a task", name.c_str());

            // 4. 处理任务 - 在获取锁的范围之外执行任务，避免长时间占用锁
            // 这里的 t 是任务对象的拷贝（或移动后的对象）
            // 假设任务类型 T 是可调用的（比如重载了 operator()），执行任务
            t();
            // 假设任务类型 T 有 ResultToString() 方法用于获取结果的字符串表示
            // 注意：这里日志打印也需要是线程安全的，依赖于 Log 库内部的锁
            LOG(DEBUG, "%s handler a task, result is: %s", name.c_str(), t.ResultToString().c_str());
        }
        LOG(INFO, "%s handler task loop end.", name.c_str());
    }

    // 禁用复制构造函数和赋值运算符，这是实现单例模式的标准做法
    // 防止通过拷贝或赋值创建 ThreadPool 的多个实例
    ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
    ThreadPool(const ThreadPool<T> &) = delete;

public:
    // === 公共静态方法：获取线程池单例实例 ===
    // 这是外部代码获取 ThreadPool 对象的唯一入口
    // 使用经典的“双重检查锁定”（Double-Checked Locking）模式来确保线程安全地创建单例
    // @return: 指向唯一的 ThreadPool<T> 单例对象的指针
    static ThreadPool<T> *GetInstance()
    {
        // 第一个检查：如果 _instance 已经被创建，直接返回，避免加锁开销
        // 这是双重检查锁定的第一个检查
        if (nullptr == _instance)
        {
            // _instance 尚未创建，需要加锁进行创建
            // 使用 LockGuard 保证锁的 RAII 管理
            LockGuard lockguard(&_lock); // 获取单例模式的互斥锁

            // 第二个检查：在获取锁后再次检查 _instance 是否已经被其他线程创建
            // 这是双重检查锁定的第二个检查
            if (nullptr == _instance)
            {
                // _instance 确实没有被创建，在此处安全地创建单例对象
                _instance = new ThreadPool<T>(); // 调用私有构造函数创建对象

                // 创建单例对象后，需要对其进行初始化和启动工作线程
                _instance->InitThreadPool(); // 初始化线程池，创建工作线程对象
                _instance->Start();          // 启动工作线程的底层线程

                LOG(DEBUG, "创建线程池单例并启动");
                return _instance; // 返回新创建的单例对象指针
            }
            LOG(DEBUG, "其他线程已创建线程池单例，获取已有实例 (内层检查后)");
        }
        LOG(DEBUG, "线程池单例已存在，直接获取");
        // 如果 _instance 已经创建，直接返回单例对象指针
        return _instance;
    }

    // === 公共方法：控制线程池的生命周期和任务管理 ===

    // 停止线程池
    // 设置运行标志为 false，并唤醒所有等待任务的线程，促使它们检查退出条件
    void Stop()
    {
        // 锁定队列，确保在修改 _isrunning 和通知时不会与工作线程冲突
        LockQueue();
        _isrunning = false; // 设置运行状态为 false，通知工作线程停止
        // 唤醒所有等待在 _cond 条件变量上的线程
        // 这样它们可以退出等待状态，重新检查 while 循环的条件，发现 _isrunning 为 false 而退出
        ThreadWakeupAll();
        // 释放锁
        UnlockQueue();
        LOG(INFO, "ThreadPool Stop() - Shutdown initiated.");
    }

    // 等待所有工作线程结束
    // 通常在调用 Stop() 后，再调用 Wait() 来确保所有线程都已退出，避免资源问题
    void Wait()
    {
        // 遍历存储工作线程对象的向量
        for (auto &thread : _threads)
        {
            // 对每个 Thread 对象调用 Join()，等待底层 POSIX 线程结束
            thread.Join(); // 会阻塞调用者，直到该工作线程结束
            LOG(INFO, "%s is quit...", thread.name().c_str());
        }
        LOG(INFO, "ThreadPool Wait() - All worker threads joined.");
    }

    // 将任务加入任务队列
    // 这是生产者调用的方法
    // @param t: 要添加的任务对象 (以常量引用方式传入，减少拷贝)
    // @return: bool，是否成功添加任务 (如果线程池已停止运行则返回 false)
    bool Enqueue(const T &t)
    {
        bool ret = false;
        // 锁定队列，保护对 _task_queue 和 _isrunning 的访问
        LockQueue();
        // 只有当线程池正在运行时才允许加入新任务
        if (_isrunning)
        {
            _task_queue.push(t); // 将任务添加到队列尾部
            // 检查是否有线程正在等待任务 (_waitnum > 0)
            // 如果有等待线程，唤醒其中一个来处理刚加入的任务
            if (_waitnum > 0)
            {
                ThreadWakeup(); // 唤醒一个等待线程
            }
            LOG(DEBUG, "enqueue task success, queue size: %zu", _task_queue.size()); // 打印队列大小方便调试
            ret = true;                                                              // 返回成功
        }
        else
        {
            LOG(WARNING, "Enqueue failed: ThreadPool is not running.");
        }
        // 释放锁
        UnlockQueue();
        return ret; // 返回添加结果
    }

    // 析构函数，清理线程池占用的资源
    // 通常在单例对象被销毁时（程序结束或手动 delete）调用
    ~ThreadPool()
    {
        // 销毁互斥锁和条件变量，释放它们占用的系统资源
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        LOG(INFO, "ThreadPool Destruct() - Mutex and Condition Variable destroyed.");
        // 注意：这里的析构函数没有删除 _instance 对象本身，单例的生命周期需要其他方式管理。
        // 也没有对 _threads 向量中的 Thread 对象进行显式操作（依赖 Thread 析构或 Join/Detach 在 Wait 中完成）。
        // 如果单例是通过 new 创建的，需要在程序的某个地方 delete _instance 来调用此析构函数。
    }

private:
    int _threadnum; // 线程池中的工作线程数量
    // std::vector<Thread> _threads; // 存储工作线程对象的向量
    //  注意：此处 std::vector<Thread> 缺失模板参数，应该是 std::vector<Thread<???>>
    //  模板参数应与 Thread::HandlerTask 的数据参数类型对应
    std::vector<Thread<std::string>> _threads; // 修改后
    std::queue<T> _task_queue;                 // 存储待执行任务的队列
    pthread_mutex_t _mutex;                    // 互斥锁，保护 _task_queue, _waitnum, _isrunning 等共享状态
    pthread_cond_t _cond;                      // 条件变量，用于工作线程等待任务
    int _waitnum;                              // 当前因任务队列为空而等待（休眠）的工作线程数量 (在锁内操作，安全)
    bool _isrunning;                           // 线程池的运行状态标志 (true 运行中, false 停止中)

    // === 单例模式的静态成员 ===
    static ThreadPool<T> *_instance; // 指向唯一的 ThreadPool<T> 单例对象的指针
    static pthread_mutex_t _lock;    // 用于保护单例对象 _instance 创建过程的互斥锁 (双重检查锁定)
};

// === 静态成员的类外初始化 ===
// 静态成员变量需要在类定义外部进行初始化
// 将 _instance 指针初始化为 nullptr
template <typename T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr;

// 初始化单例模式的互斥锁，使用宏进行静态初始化
template <typename T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;